package com.ls.spring.framework.aop.support;

import com.ls.spring.framework.aop.aspect.MyAdvice;
import com.ls.spring.framework.aop.config.MyAopConfig;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 挥之以墨
 */
public class MyAdvisedSupport {
    private MyAopConfig config;
    private Object target;
    private Class targetClass;
    private Pattern pointCutClassPattern;

    private Map<Method, Map<String, MyAdvice>> methodCache;

    public MyAdvisedSupport(MyAopConfig config) {
        this.config = config;
    }

    /**
     * 解析配置文件
     */
    private void parse() {
        // 解析Spring的表达式为java能识别的正则表达式
        String pointCut = config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        // 保存匹配Class的正则
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("public class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        // 享元模式共享池
        methodCache = new HashMap<>();
        // 保存专门匹配方法的正则
        Pattern pointCutPattern = Pattern.compile(pointCut);
        try {
            // 缓存aspect中的方法
            Class<?> aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<>();
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(), method);
            }

            // 建立目标类与advice（代理类）之间的关系
            for (Method method : this.targetClass.getMethods()) {
                String methodString = method.toString();
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pointCutPattern.matcher(methodString);
                if (matcher.matches()) {
                    Map<String, MyAdvice> advices = new HashMap<>();

                    if (StringUtils.isNotEmpty(config.getAspectBefore())) {
                        advices.put(config.getAspectBefore(), new MyAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
                    }
                    if (StringUtils.isNotEmpty(config.getAspectAfter())) {
                        advices.put(config.getAspectAfter(), new MyAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
                    }
                    if (StringUtils.isNotEmpty(config.getAspectAfterThrow())) {
                        advices.put(config.getAspectAfterThrow(), new MyAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow())));
                    }
                    methodCache.put(method, advices);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    public Map<String, MyAdvice> getAdvices(Method method) throws NoSuchMethodException {
        Map<String, MyAdvice> cache = methodCache.get(method);
        if (null == cache) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cache = methodCache.get(m);
//            this.methodCache.put( m, cache);
        }
        return cache;
    }

    public boolean pointCutMath() {
        return pointCutClassPattern.matcher(this.targetClass.toGenericString()).matches();
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        parse();
    }


}
